package binbase.web.gui

import core.MetaDataService
import binbase.web.core.BBBin
import core.BinBaseResultService
import binbase.web.core.BBDatabase

/**
 * synchronizes an experiment
 */
class SynchronizeExperimentJob {

    def searchableService
    def synchronizeDatabaseService

    def binBaseDataAccessService

    def metaDataService

    def binBaseResultService

    def grailsApplication

    def mspService

    //only one job at a time...
    def concurrent = false

    def similarityService

    def group = "binbase"
    /**
     * does the actual execution
     * @return
     */
    def execute(def context) {

        if (context.mergedJobDataMap.experimentId != null) {

            try {
                //searchableService.stopMirroring()
                String database = context.mergedJobDataMap.database.toString()

                log.info "starting synchronisation for ${database}"
                synchronizeDatabaseService.synchronizeBins(database, false)

                log.info("all bins are synchronized, ${BBBin.count()} are now in the database")

                //synchronize the experiment
                sync(context.mergedJobDataMap.experimentId.toString(), database)

                //remove bins which are not related to the experiments in this database
                if (grailsApplication.config.binbase.simplifyBins) {
                    synchronizeDatabaseService.removeUnusedBins(database)
                }

                //attach the msp files if it's enabled
                if (grailsApplication.config.binbase.mspEnabled) {
                    log.info "attaching msp file"
                    log.info(mspService.attachMspFileForDatabase(database, grailsApplication.config.binbase.mspFileName))
                }

                //calculates similarity during the job runs
                if (grailsApplication.config.binbase.similarityPreCaching) {

                    log.info("calculate bin similarities...")
                    def compareList = BBBin.findAllByDatabase(BBDatabase.findByName(database))

                    BBBin.findAllByDatabase(BBDatabase.findByName(database)).each { BBBin bin ->
                        //we only want a minimum similarity of 500
                        similarityService.calculateSimilaritiesFor(bin, 500, compareList)
                    }
                }

            }
            catch (Exception e) {
                log.error "sorry there was some kind of exception: ${e.getMessage()}"
                log.debug(e.getMessage(), e)
            }
            finally {
                //searchableService.startMirroring()
            }
        }
    }

    /**
     * does the actual synchronisation
     * @param id
     * @param database
     * @return
     */
    private def sync(id, database) {
        try {
            log.info "working on: ${id}"

            if (id != null) {

                if (!grailsApplication.config.binbase.syncMetaDataOnly) {

                    boolean areAllDataPublic = grailsApplication.config.binbase.allDataArePublic
                    synchronizeDatabaseService.synchronizeExperiment(id.toString(), database, false, areAllDataPublic)
                    log.info "finished: ${id}"

                }
                else {
                    log.info "we are only sychnronizing metadata in this run..."
                }

                log.info "sync metadata..."

                syncMetaData(database, id)

                log.info "synced metadata"

                log.info "sync results..."

                log.info "success: ${binBaseResultService.synchronizeResult(database, id)}"

                log.info "sync dsl..."

                log.info "success: ${binBaseResultService.synchronizeDSLFiles(database, id)}"

                log.info "sync datafiles..."

                log.info "success: ${binBaseResultService.synchronzieTxtFiles(database, id)}"

            }
            else {
                log.info "sorry id was a string and was not found in any of these files..."
            }
        } catch (Throwable e) {
            log.info("sorry there was an error: ${e.getMessage()}, which we ignore for now!")
            log.debug(e.getMessage(), e)

        }
    }

    /**
     * synchronizes our metadata for this expieriment and database
     * @param database
     * @param id
     * @return
     */
    private def syncMetaData(database, id) {

        boolean areAllDataPublic = grailsApplication.config.binbase.allDataArePublic

        log.info("experiment is public by default: ${areAllDataPublic}")

        /**
         * we get the meta data from the given url
         */
        if (grailsApplication.config.binbase.metaDataUrl) {
            try {
                def url = grailsApplication.config.binbase.metaDataUrl
                log.info "using service to import metadata"

                if (metaDataService.importMetaDataFromMiniXUrl(database, id, url, false, areAllDataPublic)) {

                    log.info "import using metaservice is done,skipping file based approach"
                    return
                }
                else {
                    log.info "web based service failed, try local service"
                }
            }
            catch (Exception e) {
                log.info("sorry there was an issue accessing the webservice!", e)

            }
        }
        /**
         * we get the meta data from the directory
         */
        if (grailsApplication.config.binbase.metaDataPath) {
            if (grailsApplication.config.binbase.metaDataPath instanceof Collection) {
                grailsApplication.config.binbase.metaDataPath.each { String object ->
                    getMetaFromDirectory(new File(object), database, id, areAllDataPublic)

                }
            }
            else {
                File file = new File(grailsApplication.config.binbase.metaDataPath.toString())

                getMetaFromDirectory(file, database, id, areAllDataPublic)
            }
        }
    }

    private def getMetaFromDirectory(File file, database, id, boolean areAllDataPublic) {
        if (file.exists()) {
            def f = new File(file, "${id}.xml")

            if (f.exists()) {
                try {
                    //generate the dsl file in the meta directory
                    def dsl = metaDataService.generateDSLForMetada(database, id, new XmlSlurper().parse(f))
                    def dslFile = new File(file, "${id}.dsl")
                    dslFile.write(dsl.toString())
                }
                catch (Exception e) {
                    log.info "sorry there was an error with this request: ${e.getMessage()}"

                }
                log.info "using file for metadata: ${f}"
                metaDataService.importMetaData(database, id, new XmlSlurper().parse(f), false, areAllDataPublic)


                log.info "synced metadata"
            }
            else {
                log.info "sorry metadata file: ${f} was not found"
            }
        }
        else {
            log.info("sorry we were not able to find the data directory")
        }
    }

}
